package com.yfqy.app.modules.order.director;

import com.yfqy.app.concurrency.StockService;
import com.yfqy.app.enums.ErrorCode;
import com.yfqy.app.exception.BusinessException;
import com.yfqy.app.modules.order.adapter.OrderCreateBuilderAdapter;
import com.yfqy.app.modules.order.builder.OrderInfoBuilder;
import com.yfqy.app.modules.order.context.OrderBuilderContext;
import com.yfqy.app.modules.order.dto.LockedStockDTO;
import com.yfqy.app.modules.order.dto.OrderAmountDTO;
import com.yfqy.app.modules.order.dto.OrderCreateRequest;
import com.yfqy.app.modules.order.dto.OrderInfoDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Slf4j
public abstract class AbstractOrderCreateBuilderDirector extends OrderCreateBuilderAdapter {

    @Autowired
    private StockService stockService;
    @Autowired
    private OrderInfoBuilder orderInfoBuilder;

    /**
     * 验证下单参数
     *
     * @param request
     */
    public abstract void validRequiredParam(OrderCreateRequest request);


    /**
     * 由具体的子类订单类型计算订单金额
     *
     * @param request
     * @return
     */
    public abstract OrderAmountDTO calculateAmount(OrderCreateRequest request);

    /**
     * 执行创建订单流程
     *
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderInfoDTO createOrder(OrderCreateRequest request, OrderAmountDTO amount) {
        LockedStockDTO lockedStockDTO = null;
        OrderInfoDTO orderInfoDTO;
        try {
            // 生成订单数据
            orderInfoDTO = orderInfoBuilder.buildOrder(request, amount);
            if (!request.isPreview()) {
                // 锁定库存
                lockedStockDTO = lockStock(request);
                // 保存订单
                saveOrder(orderInfoDTO);
            }
        } catch (Exception e) {
            // Redis锁定库存回滚
            if (lockedStockDTO != null) {
                boolean rollBackedMultiStock = stockService.rollBackedMultiStock(lockedStockDTO.getSkuNumbers(), lockedStockDTO.getPrefix());
                log.info("原子性批量回滚库存-rollBackedMultiStock={}", rollBackedMultiStock);
            }
            if (e instanceof BusinessException) {
                throw e;
            }
            throw new BusinessException(ErrorCode.SYSTEM_ERROR.getCode(), e.getMessage());
        } finally {
            OrderBuilderContext.reset();
        }

        return orderInfoDTO;
    }
}
